Apple II
Technical Notes
_____________________________________________________________________________
                                                  Developer Technical Support


Apple IIGS
#22:    Proper Unloading of Dynamic Segments

Revised by:    Lou Infeld, Matt Deatherage,
               Jim Mensch & Eric Soldan                         November 1988
Written by:    Guillermo Ortiz                                   October 1987

This Technical Note discusses strategies that programs may use to deal with 
dynamic segments.
_____________________________________________________________________________

An application which uses dynamic segments must unload them by hand when it 
needs the memory they occupy since the system does not automatically release 
this memory.  This requirement poses some interesting problems for any 
developer using dynamic segments to ensure that an application runs within a 
given memory configuration, and this Technical Note presents the issues and 
some possible avenues for attacking the problems.


Which Segments to Unload?

Unloading One Segment at a Time

First, we have a global programming problem:  what to do when the application 
needs more memory and cannot get it?  The normal impulse, unloading arbitrary 
dynamic segments until we satisfy the memory need, presents another question:  
how does the application recognize which segments are loaded but have not 
participated in the current chain of events?  In other words, how can the 
application be sure it does not have to return control to a segment it wants 
to unload?

Since the system provides no mechanism to track the chain of active calls, you 
need to program the application to allow for such bookkeeping.  Some 
alternatives are as follows:

1.    Eliminate interdependencies between dynamic segments.  Thus, a 
      subroutine in one segment can be sure that code from another, 
      disposable, segment has not called it.  For instance, you can 
      often make initialization code fully independent of code in other 
      segments.

2.    Write dynamic segments in such a way that there is only one entry 
      point to each one and establish a list of counters in the main 
      static segment; every time the entry point gets control it 
      increments the counter, and it decreases the counter every time it 
      passes back control.  If at any given moment the application needs 
      more memory, it can check the list and unload those segments with 
      empty (zero) counters.

It is possible to imagine more complicated scenarios, such as how to recover 
if the segments you need to load fragment memory in such a way that there is 
not enough contiguous memory available to satisfy allocation needs.  We leave 
these possibilities as exercises for the curious reader.

Unloading All Segments at Once

Another technique is to unload all dynamic segments in the main logic of the 
program.  You can accomplish this with one Unload Segment By Number ($0C) call 
specifying zero for the load segment number.  Unloading all dynamic segments 
is not as inefficient as it sounds because the System Loader does not reload a 
needed dynamic segment from the corresponding load file if the system has not 
yet purged it from memory.


How Best to Unload the Segments

Obtaining the Address of a Dynamic Segment

Once we know what we want to unload, we have the more specific problem of 
deciding which call to use in unloading the segment.  One option is, very 
appropriately, Unload Segment ($0E).  However, the APW Assembler may object to 
references to dynamic segments other than a JSL unless you tell it otherwise.

The APW Assembler uses the DYNCHK directive to allow access to labels or data 
in dynamic segments without generating errors.  With DYNCHK ON, the APW 
assembler gives an error message if you access any label in a dynamic segment 
with any instruction other than JSL.  Turning DYNCHK OFF allows you to make 
any reference to dynamic segment labels without error, although this is 
usually not valid (see below).  DYNCHK OFF allows code like the following to 
assemble properly:

    Pushlong    #DynSegAddr    ; push the jump table address
    _UnloadSegment             ; and unload the segment!

It is then your responsibility to ensure that this code is not executed unless 
that dynamic segment is currently loaded.  Note that DYNCHK OFF also causes 
CODECHK OFF, thus APW does not tell you if you accidentally use JSR to address 
a label in a different (static or dynamic) segment.  Checking for this is your 
responsibility.  Also note that there is no equivalent of DYNCHK in the MPW 
IIGS cross-development system; the default is the equivalent of DYNCHK OFF.

If you choose to leave DYNCHK ON, you have to use other methods to find the 
address of a label in a dynamic segment.  Below is an example which shows a 
way of getting the address of a dynamic segment which will link correctly with 
DYNCHK ON.  Suppose we have the following code in a static segment:

    LABEL    JSL FOO

If FOO is in a dynamic segment, then we can obtain the address of FOO at run 
time using the following code:

    LDA LABEL+1
    STA ADDRESS
    LDA LABEL+3
    STA ADDRESS+2

The address obtained in this way is the address of the jump table entry for 
FOO .  You can use this address in the Unload Segment call since the System 
Loader recognizes the address as a jump table address.  Note that since the 
address is a jump table entry, nothing other than passing control to this 
address is valid.  Doing any other kinds of operations (like loading or 
storing data) at that label causes problems.  LDA DynSegAddr does not give you 
the first byte at DynSegAddr, but rather the first byte of the jump table to 
DynSegAddr.  Also note that good programming practice dictates that all access 
to dynamic segments occur via JSL instructions.

If you decide which segments to unload using the scheme described in 
alternative two above, you again have several options.  Most of these options 
depend upon your preferences for the DYNCHK directive.  If you choose to turn 
DYNCHK OFF, you can actually code a table of segment addresses into your 
program.  The System Loader patches the address at the time of execution with 
jump-table addresses, and you can unload segments that way.  If you leave 
DYNCHK ON, you must obtain the address of each dynamic segment at the time of 
execution yourself.

We presented an assembly language example of this earlier.  The following code 
segment demonstrates how a function in C can return the address of its 
location in RAM:

    char *Seg2Address()
    {
        char *address
        asm
        {
            phk
            phk
            lda #Seg2Address
            sta address
            pla
            and 0x00FF
            sta address+2
        }
        return(address);
    }

Note that the glue in APW C 1.0 for Unload Segment is broken and actually 
calls Unload Segment By Number.  This is fixed in APW C 1.0.1 and later and in 
all versions of MPW IIGS C.

Using Unload Segment By Number

As mentioned earlier, you can also use Unload Segment By Number ($0C) to 
unload dynamic segments.  This call takes user ID, load file number, and load 
segment number as parameters, and it is reasonable to assume at this time that 
the load file number has a value of one for most programs.  The trick here is 
getting to know, in advance, the load segment number of any segment the 
application may want to dispose of at a specified moment during its execution.

The idea of a table again comes to mind since each dynamic segment could store 
its own number there the first time its gets control for all the other 
segments to see.  In this case, it is also possible for the application to 
initialize the table with the segment numbers.  An application can determine 
its own load segment numbers when you compile it since you can specify into 
which load segment you wish to place any object segment.  However, you must 
exercise care when linking to preserve this order.

Unloading Segments Before Running Out of Memory

If all the dynamic segments are independent modules, it is possible to 
implement a second approach:  instead of waiting until the application runs 
out of memory to begin unloading segments, unload each segment immediately 
after it returns control to the code that called it.  With this approach, you 
cut the application's overhead and know that at any given moment, you have the 
maximum amount of memory available.  The System Loader introduces some extra 
overhead of its own of course, but if the segment is still in memory, it 
should not be too much.  The following code example illustrates this 
technique:


    JSL    DynSegAddr          ; call the dynamic segment
                               ; (it does its work before returning)
    Pushlong    #DynSegAddr    ; push the jump table address
    _UnloadSegment             ; and unload the segment!


Further Reference
o    Apple IIGS Programmer's Workshop Assembler Reference
o    ProDOS 16 Technical Reference
o    GS/OS Reference, Volume 2
